/*
*   Segundo Trabalho: SCC-0143
*
*   Palíndromos:
*       Verifica a ocorrência de palíndromos de palavras e frases em um texto;
*       As bibliotecas OpenMP e MPI são utilizadas para a paralelização dos processos.
*/

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <time.h>
#include <math.h>
#include <omp.h>
#include <ctype.h>

int ispalindromo(char *string);
int tamArquivo(FILE *f);
int contPalindromo(char *f, int tamIni, int tamMax, int *primos);
int crivo (int n);

typedef struct{
    int *operacoes;
    int numThreads;
}Thread;

/*
*   Função main:
*       Define a quantidade de threads para a execução;
*       Dessa maneira, o texto é dividido pela quantidade de threads, e assim cada thread é responsável por
*       achar a quantidade de palíndromos naquela parte do texto.
*       Após isso, a quantidade de números primos são calculados.
*/

int main(){

	int cont = 0;
	int tam;
	int i;
	int quant_primos = 0;
	double time;
	clock_t start;
	clock_t end;

	Thread th;
    th.numThreads = 4;

    th.operacoes = (int*) malloc(th.numThreads*sizeof(int));

	FILE *arq;
	arq = fopen("wikipedia.txt","r");
	if(arq == NULL)
		printf("Falha ao abrir o arquivo");
	else{

		char *text;

		tam = tamArquivo(arq);
		text = malloc(tam*sizeof(char));

		fread(text, 1, tam, arq);

		start = clock();

        /*  Iteração que dividi o texto pela quantidade de threads;
        *   Cada thread irá verificar a quantidade de palindromos e verificar se são primos;
        *   Após todas as threads serem finalizadas, é retornado o total de palíndromos e números primos.
        */
        omp_set_num_threads(th.numThreads);

        #pragma omp parallel for
        for(i=0;i<th.numThreads;i++){
            cont += contPalindromo(text,i*(tam/th.numThreads),(i+1)*(tam/th.numThreads), &quant_primos);
            (th.operacoes[omp_get_thread_num()])++;
		}

		end = clock();
	}

	printf("Total Palindromos: %d\n",cont);
	printf("Total Palindromos/Primos: %d\n",quant_primos);

	for(i=0; i < th.numThreads; i++)
        printf("Thread %d: %d\n", i,tamArquivo(arq)/th.numThreads);

	time = ((double)(end-start)/(double)CLOCKS_PER_SEC);
	printf("tempo é %lf\n", time);

	return 0;
}

/*  Verifica se uma palavra é um palíndromo */
int ispalindromo(char *string){


	int i=0;
	int tam = strlen(string);

	if( tam%2 == 0 ){

		for(i = 0 ; i < tam/2 ; i++){

			if(string[i] != string[tam-1-i])
				return 0;

		}
	return 1;
	}
	else{
		while( i != tam/2){
			if(string[i] != string[tam-1-i])
				return 0;
			i++;
		}

	return 1;
	}
}

/*Retorna o tamanho do arquivo*/
int tamArquivo(FILE *f){

	int size = 0;
	fseek(f,0,SEEK_END);
	size = ftell(f);
	fseek(f,0,SEEK_SET);
	return size;

}

/*Verifica se um número é primo*/
int Crivo (int n){

    int i,j,k; /*contadores auxiliares*/
    int limite; /*valor limite*/
    int primo;
    int *lista; /*lista de numeros*/

    /*para os numeros 1,2 e 3*/
    if(n==1){
        return 1;
    }

    if(n==2){
        return 2;
    }

    if(n==3){
        return 3;
    }
    /*para os numeros maiores que 3*/
    else{

        lista = (int*) malloc((n-1)*sizeof(int)); /*aloca a lista*/

        for (i=0; i < n-1; i++){ /*preenche com os valores de 2 até n*/
            lista[i] = i+2;
        }

        limite = sqrt(n); /*calcula o valor limite*/

        i = 0;

        while(lista[i] <= limite){
            primo = lista[i];
            for(j=i+1; j<n-1; j++){
                if(lista[j] % primo == 0){ /*caso não seja primo, elimina, organiza a lista e diminui o n*/
                    for(k=j;k<n-1;k++){
                        lista[k] = lista[k+1];
                    }
                    n--;
                }
            }
            i++;

        }

        return lista[n-2];
    }

}

int contPalindromo(char *text, int tamIni, int tamMax, int *primos){

	char word[90];
	int stop = 0;
	int cont = 0;
	int percorreText = tamIni+1;
	int i = 0;
	int num_primo = 0;

	while(!stop){

		if(percorreText == tamMax){
			stop = 1;
		}else{

			if(isalnum(text[percorreText])){
				word[i] = text[percorreText];
				num_primo += text[percorreText];
				i++;
			}
			else{
				word[i] = '\0';

				if(ispalindromo(word) && i >= 2){
					cont++;
					if(Crivo(num_primo)==num_primo){
                        (*primos)++;
                    }
				}
				i = 0;
				num_primo = 0;
			}

		}
		percorreText++;
	}
	return cont;
}
